import { consts } from "@/module-00-codebase/pkg-00-const";
import { Throwable } from "@/module-00-codebase/pkg-02-tool";
export class DataExchange {
  public parse(s: string) {
    const args = Array.from(arguments) as any;
    let res = JSON.parse.apply(JSON, args);
    return parseCircular(res);
  }
  public serialize(o: any) {
    const args = Array.from(arguments) as any;
    args[0] = serializeCircular(args[0]);
    return JSON.stringify.apply(JSON, args);
  }
}
export const initializing = async () => {
  const oriStringify = JSON.stringify;
  const oriParse = JSON.parse;
  const serialize = serializeCircular;
  const parse = parseCircular;
  JSON.stringify = function (this: any) {
    const args = Array.from(arguments) as any;
    args[0] = serialize(args[0]);
    return oriStringify.apply(this, args);
  } as any;
  JSON.parse = function (this: any) {
    const args = Array.from(arguments) as any;
    let res = oriParse.apply(this, args);
    return parse(res);
  } as any;
};
export const order = () => consts.firstOrder;

const serializeCircular = (root: any): any => {
  const map = new Map();
  map.set(root, "$");
  if (Array.isArray(root)) {
    let result = [] as any[];
    for (let i = 0; i < root.length; i++) {
      __serializeCircular(root, "$", i, map, result);
    }
    return result;
  } else if (null !== root && typeof root === "object") {
    let result = {};
    Object.keys(root).forEach(key => {
      __serializeCircular(root, "$", key, map, result);
    });
    return result;
  } else {
    return root;
  }
};
const parseCircular = (root: any): any => {
  if (Array.isArray(root)) {
    for (let i = 0; i < root.length; i++) {
      __parseCircular(root, root, i, null);
    }
  } else if (null !== root && typeof root === "object") {
    Object.keys(root).forEach(key => {
      __parseCircular(root, root, key, null);
    });
  }
  return root;
};
const __parseCircular = (root: any, parent: any, objkey: string | number, parentparent: any) => {
  const obj = parent[objkey];
  if (null === obj || typeof obj !== "object") {
    //
  } else if (Array.isArray(obj)) {
    for (let i = 0; i < obj.length; i++) {
      __parseCircular(root, obj, i, parent);
    }
  } else if (!!obj["$ref"]) {
    let path = obj["$ref"] as string;
    let firstChar = path.substring(0, 1);
    if (path === "..") {
      parent[objkey] = parentparent;
    } else if (firstChar === "@") {
      parent[objkey] = parent;
    } else if (firstChar === "$") {
      let paths = path
        .substring(1)
        .split(/\.|\[|\]/)
        .filter(s => !!s)
        .map(s => s.replace(/\\_/g, "_"));
      parent[objkey] = paths.reduce((a, b) => a[b], root);
    } else {
      Throwable(`Unrecognize $ref=${path}`);
    }
  } else {
    Object.keys(obj).forEach(key => {
      __parseCircular(root, obj, key, parent);
    });
  }
};
const __serializeCircular = (parent: any, base: string, objkey: string | number, obj_key_map: Map<string, any>, result: any) => {
  const obj = parent[objkey];
  if (null === obj || typeof obj !== "object") {
    result[objkey] = obj;
  } else if (obj_key_map.has(obj)) {
    result[objkey] = { $ref: obj_key_map.get(obj) };
  } else {
    const endFix = Array.isArray(parent) ? `[${objkey}]` : `.${objkey}`;
    let objrefstr = `${base}${endFix}`;
    obj_key_map.set(obj, objrefstr);
    if (Array.isArray(obj)) {
      result = result[objkey] = [];
      for (let i = 0; i < obj.length; i++) {
        __serializeCircular(obj, objrefstr, i, obj_key_map, result);
      }
    } else {
      result = result[objkey] = {};
      Object.keys(obj).forEach(key => {
        __serializeCircular(obj, objrefstr, key, obj_key_map, result);
      });
    }
  }
};
